
module ctpl.all ;

package import 
	ctpl.plugin;
	
package import 
	std.array,
	std.string,
	std.conv;

version = PLUGIN_DEBUG1 ;


string cstring_dup(char* s){
	int i =0 ;
	while( s[i] !is 0 ) i++;
	string ret = s[0..i+1].idup ;
	return ret[0..i] ;
}

uint atoi(T) (T[] s, int radix = 10){
        uint value;
        foreach (c; s)
                 if (c >= '0' && c <= '9')
                     value = value * radix + (c - '0');
                 else
                    break;
        return value;
}

/// compile time integer to string
string ctfe_i2a(int i){
    char[] digit	= cast(char[]) "0123456789";
    char[] res		= cast(char[]) "";
    if (i==0){
        return  "0" ;
    }
    bool neg=false;
    if (i<0){
        neg=true;
        i=-i;
    }
    while (i>0) {
        res=digit[i%10]~res;
        i/=10;
    }
    if (neg)
        return cast( string) ( '-' ~res );
    else
        return cast( string) res;
}

class CTpl_Member {
	ptrdiff_t	id ;
	ptrdiff_t	offset ;
	ptrdiff_t	size ;
	string		type, name, tyid, loc ;
	CTpl		_parent ;
	
	enum Index {
		Name ,
		Loc ,
		Type ,
		TypeID ,
		Size ,
	}
	
	alias typeof(_parent) _Parent_Type ;
	
	string index_message(){
		return _parent._tpl_name ~ ":" ~ name ~ ":" ~ ctfe_i2a(id) ~ ":" ~ ctfe_i2a(offset) ~ ":" ~  ctfe_i2a(size) ;
	}
	
	public this(_Parent_Type p, char[][] args){
		string member_name	= cstring_dup(args[ Index.Name ].ptr) ;
		
		auto _pmember	= member_name in p._tpl_members ;
		if( _pmember !is null ){
			return  ;
		}
		
		id	= p._tpl_members.length ;
		
		name	= member_name ;
		type	= cstring_dup(args[Index.Type ].ptr) ;
		tyid	= cstring_dup(args[Index.TypeID].ptr) ;
		size	= atoi(args[Index.Size]) ;
		loc	= cstring_dup(args[ Index.Loc ].ptr) ;
		_parent	= p ;
		offset	= p._offset ;
		
		int _size	= size ;
	
		while(  _size > 0 ) {
			p._offset	+= size_t.sizeof ;
			_size		-= size_t.sizeof ;
		}
		
		p._tpl_members[member_name]	= this ;
		version(PLUGIN_DEBUG)
			dmd_printf("new member tpl(%x).name=`%s` member=`%s` id=%d  tyid=`%s` size=%d offset=%d loc=`%s` \n", p, p._tpl_name.ptr, name.ptr, id, tyid.ptr, size, offset, loc.ptr );
	}
	
}

class CTpl {
	static __gshared _tpl_protocol	= "tpl://" ;
	static __gshared _ERR_message	= cast(char[]) "tpl::error" ;
	static __gshared _OK_message	= cast(char[]) "tpl::ok" ;
	
	alias  typeof(this)	This ;
	static __gshared This[string]	tpl_instances ;
	
	static char[] ImportInvoke(char[] _argument){
		if( _argument is null || _argument.length < _tpl_protocol.length || _argument[0.._tpl_protocol.length] != _tpl_protocol ){
			return null ;
		}
		auto args	= cast(char[][]) std.array.split(_argument[_tpl_protocol.length..$], "::");
		if( args.length < 2 ) {
			return null ;
		}
		
		for(int i = _tpl_protocol.length; i < _argument.length;i++){
			if( _argument[i] is ':' ){
				int j = i+1 ;
				if( j < _argument.length && _argument[j] is ':' ){
					_argument[i]	= 0 ;
					i++;
				}
			}
		}
		
		
		switch( args[0] ) {
			case "new":
				This _this = Tpl_New(args, true) ; 
				return  _this is null ? null : _OK_message;
			case "assign":
				return Tpl_Assign(args);
			default:
				dmd_printf("Err:: invalid call `tpl::%s`\n", args[0].ptr );
				return null ;
		}
		
		return null ;
	}
	
	static This Tpl_New(char[][] args, bool create = false ) {
		
		char[][] _args	= cast(char[][]) std.array.split(args[1], ":");
		
		foreach(int i, ref c; args[1]) {
			if( c is ':' ) c = '\0' ;
		}
		
		string _tpl_name = cast(string) _args[0] ;
		
		auto _pthis	= _tpl_name in tpl_instances ;
		
		This _this ;
		
		if( _pthis is null ) {
			if( !create ) {
				return null ;
			}
			_this	= new CTpl(_args[0], _args[1] );
		
			tpl_instances[ _tpl_name.idup ] = _this ;
			
			version(PLUGIN_DEBUG)
				dmd_printf("New Template (%s:%s) \n", _args[0].ptr, _args[1].ptr );
			
		} else {
			_this	= *_pthis ;
			version(PLUGIN_DEBUG)
				dmd_printf("New Template (%s:%s) Find exists Template (%s:%s)  ,  \n", _args[0].ptr, _args[1].ptr, _this._tpl_name.ptr, _this._tpl_loc.ptr );
			
			if( _this._tpl_name != _tpl_name ) {
				dmd_printf("inner error %d \n", __LINE__ );
				return null ;
			}
			
		}
		
		return _this ;
	}
	
	static char[] Tpl_Assign(char[][] args) {
		
		This _this	= Tpl_New(args);
	
		auto _args	= cast(char[][]) std.array.split(args[2], ":" ) ;
		
		if( _args is null || _args.length !is 5 ){
			dmd_printf("tpl::assign invalid arguments number:%d  = `%s`\n", _args.length,  _args[0].ptr );
			return null ;
		}
		foreach(ref c;args[2]) if(c is ':') c = 0;
		
		
		auto member	= _this.FindMember( _args ) ;
		if( member is null ) {
			return null ;
		}
		
		return cast(char[]) member.index_message() ;
	}
	
	
	string 		_tpl_name ;
	string 		_tpl_loc ;
	CTpl_Member[string]	_tpl_members ;
	size_t		_offset ;
	
	public this(char[] name, char[] loc) {
		_tpl_name	= cstring_dup( name.ptr ) ;
		_tpl_loc	= cstring_dup( loc.ptr ) ;
	}
	
	private CTpl_Member FindMember(char[][] args){
		CTpl_Member member	= null ;
		foreach( _member; this._tpl_members){
			if( _member.name == args[0] ) {
				member	= _member ;
				break ;
			}
		}

		if( member is null ) {
			member		= new CTpl_Member(this, args) ;
		} else {
			// assert the member is same
			version(PLUGIN_DEBUG)
				dmd_printf("existss member\n");
			if( member.name != args[ CTpl_Member.Index.Name ] ) {
				dmd_printf("tpl:assign (%p)(%s) inner error ln:%d \n", this, this._tpl_name.ptr, __LINE__);
				return null ;
			}
			
			if( member.tyid != args[ CTpl_Member.Index.TypeID ] ) {
				dmd_printf("tpl:assign (%p)(%s) type conflict (%s:%s=%s)(%s) with (%s:%s=%s)(%s) \n", this, this._tpl_name.ptr, member.type.ptr , member.name.ptr , member.loc.ptr , member.tyid.ptr , args[CTpl_Member.Index.Type].ptr, args[CTpl_Member.Index.Name].ptr,  args[CTpl_Member.Index.Loc].ptr, args[CTpl_Member.Index.TypeID ].ptr );
				return null ;
			}
			
			if( member.size != atoi(args[CTpl_Member.Index.Size ]) ) {
				dmd_printf("tpl:assign  (%p)(%s) error size \n", this, this._tpl_name.ptr);
				return null ;
			}
		}
		
		return member ;
	}
	
}